package org.dyndns.opendemogroup.optimizer;

import java.util.Random;

// TODO: should Member implement Serializable?
// TODO: add toXml?
// TODO: add Collection<Parameter> getParameters() which caches results
// (on a per-problem class basis)
/**
 * Represents an instance in the population, by encoding its genome and
 * providing some very basic operations.
 */
public class Member implements Comparable<Member>, Cloneable
{
	private boolean knowsFitness = false;

	private double fitness;

	private int memberSize;

	public double[] genome;

	public Member ( int memberSize )
	{
		this.memberSize = memberSize;
		genome = new double[memberSize];
	}

	public double getFitness ( )
	{
		if ( !knowsFitness )
		{
			throw new IllegalStateException (
				"The fitness has not been computed!" );
		}
		return fitness;
	}

	public Object clone ( ) throws CloneNotSupportedException
	{
		Member result = (Member) super.clone ( );
		result.genome = genome.clone ( );
		return result;
	}

	public int compareTo ( Member o )
	{
		getFitness ( );
		double foreignFitness = o.getFitness ( );
		if ( this.fitness > foreignFitness )
		{
			return 1;
		}
		else if ( this.fitness < foreignFitness )
		{
			return -1;
		}
		return 0;
	}

	public boolean decode ( IOptimizationProblem target, Random randomSource )
	{
		// first check if target implements ICustomCoding
		if ( target instanceof ICustomCoding )
		{
			ICustomCoding coding = (ICustomCoding) target;
			coding.decodeFrom ( this );
		}
		else
		{
			// TODO: Implement smart reflection- and attribute-based decoding
		}

		boolean targetChanged = target.makeValid ( );

		fitness = target.computeFitness ( randomSource );
		knowsFitness = true;
		return targetChanged;
	}

	public void encode ( IOptimizationProblem source )
	{
		// first check if target implements ICustomCoding
		if ( source instanceof ICustomCoding )
		{
			ICustomCoding coding = (ICustomCoding) source;
			coding.encodeTo ( this );
		}
		else
		{
			// TODO: Implement smart reflection- and attribute-based encoding
		}
	}

	// TODO: add toString overload with a IOptimizationProblem source parameter
	public String toString ( )
	{
		// '[' + (2 * memberSize - 1) + ']'
		StringBuilder sb = new StringBuilder ( 1 + ( memberSize * 2 ) - 1 + 1 );
		sb.append ( '[' );
		for ( int i = 0; i < memberSize; i++ )
		{
			sb.append ( genome[i] );
			if ( i < memberSize - 1 )
			{
				sb.append ( ' ' );
			}
		}
		sb.append ( ']' );
		return sb.toString ( );
	}

	public void copyFrom ( Member source, int startIndex, int endIndex )
	{
		assert source != null;
		assert startIndex >= 0;
		assert endIndex < memberSize;
		ArrayUtil.copy ( source.genome, genome, startIndex, endIndex );
		knowsFitness = false;
	}

	public int getMemberSize ( )
	{
		return memberSize;
	}
}
